package pl.edu.agh.intobl.utils;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import pl.edu.agh.intobl.Customer;
import pl.edu.agh.intobl.CustomersList;
import pl.edu.agh.intobl.exceptions.BadArgumentException;
import pl.edu.agh.intobl.exceptions.BadFileFormatException;

/**
 * Utility class for reading and obtaining input data
 */
public class InputParser {

	final Integer vehiclesCapacity;
	final CustomersList customers;

	private static final int ROW_LENGTH = 7;

	/**
	 * Default constructor. Parses input file and stores all parameters in
	 * fields
	 * 
	 * @param file
	 *            input file filled with input data. Handled file format:
	 *            <vehicles capacity> (integer) N lines with customer
	 *            description as following <customer no> <X coord> <Y coord>
	 *            <demand> <ready time> <due time> <service time> each of the
	 *            value is an integer
	 * 
	 * @throws BadArgumentException
	 *             thrown when passed argument is null or given file couldn't
	 *             been found
	 * @throws BadFileFormatException
	 *             thrown when file format constraints aren't abided
	 */
	public InputParser(File file) throws BadArgumentException,
			BadFileFormatException {
		if (file == null)
			throw new BadArgumentException("file cannot be null");

		BufferedReader reader = null;
		try {
			reader = new BufferedReader(new FileReader(file));
		} catch (FileNotFoundException e) {
			throw new BadArgumentException(e);
		}

		try {
			vehiclesCapacity = parseVehiclesCapacity(reader.readLine());
			List<Customer> customerDescriptions = parseCustomerDescriptions(reader);
			customers = new CustomersList(customerDescriptions);

		} catch (IOException e) {
			throw new BadFileFormatException(
					"An IO exception occured during parsing input file");
		} catch (NumberFormatException e) {
			throw new BadFileFormatException(
					"either vehicle capacity, number of vehicles or the distance from client "
							+ "to client is not an integer");
		} finally {
			try {
				reader.close();
			} catch (IOException e) {
				// whatever
			}
		}
	}

	/**
	 * @param input
	 *            reader
	 * @return list of parsed customers
	 * @throws BadFileFormatException
	 * @throws IOException
	 */
	private List<Customer> parseCustomerDescriptions(BufferedReader reader)
			throws BadFileFormatException, IOException {
		String line = null;
		List<Customer> customersList = new ArrayList<Customer>();
		int id = 0;
		while ((line = reader.readLine()) != null) {
			String[] row = line.split("\\s+");
			if (row.length != ROW_LENGTH) {
				throw new BadFileFormatException(
						"Each row length has to be equal to " + ROW_LENGTH);
			}
			try {
				Integer label = Integer.parseInt(row[0]);
				Integer x = Integer.parseInt(row[1]);
				Integer y = Integer.parseInt(row[2]);
				Integer demand = Integer.parseInt(row[3]);
				Integer readyTime = Integer.parseInt(row[4]);
				Integer dueDate = Integer.parseInt(row[5]);
				Integer serviceTime = Integer.parseInt(row[6]);
				Customer customer = new Customer(id, label, x, y, demand,
						readyTime, dueDate, serviceTime);
				customersList.add(customer);
				id++;
			} catch (NumberFormatException ex) {
				throw new BadFileFormatException(
						"All of the parameters must be integers");
			}
		}
		if (id < 2) {
			throw new BadFileFormatException(
					"There must be at least the depot and one customer provided");
		}
		return customersList;
	}

	/**
	 * @return vehiclesCapacity
	 */
	public int getVehiclesCapacity() {
		return vehiclesCapacity;
	}

	/**
	 * @param vehiclesDataLine
	 * @return capacity
	 * @throws BadFileFormatException
	 */
	private Integer parseVehiclesCapacity(String vehiclesDataLine)
			throws BadFileFormatException {
		Integer vehiclesCapacity = Integer.parseInt(vehiclesDataLine.trim());

		if (vehiclesCapacity < 1) {
			throw new BadFileFormatException(
					"Capacity of vehicles needs to be positive integer");
		}

		return vehiclesCapacity;
	}

	/**
	 * @return customers on CustomersList object, containing distance matrix
	 */
	public CustomersList getCustomers() {
		return customers;
	}
}
